/// Size of the state array in bits
const B: usize = 1600;

const W: usize = B / 25;
const L: usize = W.ilog2() as usize;

const U8BITS: usize = u8::BITS as usize;

// Macro for looping through the whole state array
macro_rules! iterate {
    ( $x:ident, $y:ident, $z:ident => $b:block ) => {
        for $y in 0..5 {
            for $x in 0..5 {
                for $z in 0..W {
                    $b
                }
            }
        }
    };
}

/// A function that produces a padding string such that the length of the padding + the length of
/// the string to be padded (2nd parameter) is divisible by the 1st parameter
type PadFn = fn(isize, isize) -> Vec<bool>;
type SpongeFn = fn(&[bool]) -> [bool; B];

type State = [[[bool; W]; 5]; 5];

fn state_new() -> State {
    [[[false; W]; 5]; 5]
}

fn state_fill(dest: &mut State, bits: &[bool]) {
    let mut i = 0usize;

    iterate!(x, y, z => {
        if i >= bits.len() { return; }
        dest[x][y][z] = bits[i];
        i += 1;
    });
}

fn state_copy(dest: &mut State, src: &State) {
    iterate!(x, y, z => {
        dest[x][y][z] = src[x][y][z];
    });
}

fn state_dump(state: &State) -> [bool; B] {
    let mut bits = [false; B];

    let mut i = 0usize;

    iterate!(x, y, z => {
        bits[i] = state[x][y][z];
        i += 1;
    });

    bits
}

/// XORs the state with the parities of two columns in the state array
fn theta(state: &mut State) {
    let mut c = [[false; W]; 5];
    let mut d = [[false; W]; 5];

    // Assign values of C[x,z]
    for x in 0..5 {
        for z in 0..W {
            c[x][z] = state[x][0][z];

            for y in 1..5 {
                c[x][z] ^= state[x][y][z];
            }
        }
    }

    // Assign values of D[x,z]
    for x in 0..5 {
        for z in 0..W {
            let x1 = (x as isize - 1).rem_euclid(5) as usize;
            let z2 = (z as isize - 1).rem_euclid(W as isize) as usize;

            d[x][z] = c[x1][z] ^ c[(x + 1) % 5][z2];
        }
    }

    // Xor values of D[x,z] into our state array
    iterate!(x, y, z => {
        state[x][y][z] ^= d[x][z];
    });
}

/// Rotates each lane by an offset depending of the x and y indeces
fn rho(state: &mut State) {
    let mut new_state = state_new();

    for z in 0..W {
        new_state[0][0][z] = state[0][0][z];
    }

    let mut x = 1;
    let mut y = 0;

    for t in 0..=23isize {
        for z in 0..W {
            let z_offset: isize = ((t + 1) * (t + 2)) / 2;
            let new_z = (z as isize - z_offset).rem_euclid(W as isize) as usize;

            new_state[x][y][z] = state[x][y][new_z];
        }

        let old_y = y;
        y = ((2 * x) + (3 * y)) % 5;
        x = old_y;
    }

    state_copy(state, &new_state);
}

/// Rearrange the positions of the lanes of the state array
fn pi(state: &mut State) {
    let mut new_state = state_new();

    iterate!(x, y, z => {
        new_state[x][y][z] = state[(x + (3 * y)) % 5][x][z];
    });

    state_copy(state, &new_state);
}

fn chi(state: &mut State) {
    let mut new_state = state_new();

    iterate!(x, y, z => {
        new_state[x][y][z] = state[x][y][z] ^ ((state[(x + 1) % 5][y][z] ^ true) & state[(x + 2) % 5][y][z]);
    });

    state_copy(state, &new_state);
}

/// Calculates the round constant depending on what the round number is
fn rc(t: u8) -> bool {
    let mut b1: u16;
    let mut b2: u16;
    let mut r: u16 = 0x80; // tread r as an array of bits

    //if t % 0xFF == 0 { return true; }

    for _i in 0..(t % 255) {
        b1 = r >> 8;
        b2 = r & 1;
        r |= (b1 ^ b2) << 8;

        b1 = (r >> 4) & 1;
        r &= 0x1EF; // clear r[4]
        r |= (b1 ^ b2) << 4;

        b1 = (r >> 3) & 1;
        r &= 0x1F7; // clear r[3]
        r |= (b1 ^ b2) << 3;

        b1 = (r >> 2) & 1;
        r &= 0x1FB; // clear r[2]
        r |= (b1 ^ b2) << 2;

        r >>= 1;
    }

    (r >> 7) != 0
}

/// Applies the round constant to the first lane of the state array
fn iota(state: &mut State, i_r: u8) {
    let mut rc_arr = [false; W];

    for j in 0..=L {
        rc_arr[(1 << j) - 1] = rc((j as u8) + (7 * i_r));
    }

    for (z, bit) in rc_arr.iter().enumerate() {
        state[0][0][z] ^= *bit;
    }
}

fn rnd(state: &mut State, i_r: u8) {
    theta(state);
    rho(state);
    pi(state);
    chi(state);
    iota(state, i_r);
}

fn keccak_f(bits: &[bool]) -> [bool; B] {
    let n_r = 12 + (2 * L);

    let mut state = state_new();
    state_fill(&mut state, bits);

    for i_r in 0..n_r {
        rnd(&mut state, i_r as u8);
    }

    state_dump(&state)
}

fn pad101(x: isize, m: isize) -> Vec<bool> {
    let mut j = -m - 2;

    while j < 0 {
        j += x;
    }

    j %= x;

    let mut ret = vec![false; (j as usize) + 2];
    *ret.first_mut().unwrap() = true;
    *ret.last_mut().unwrap() = true;

    ret
}

/// Sponge construction is a method of compression needing 1) a function on fixed-length bit
/// strings( here we use keccak_f), 2) a padding function (pad10*1), and 3) a rate. The input and
/// output of this method can be arbitrarily long
fn sponge(f: SpongeFn, pad: PadFn, r: usize, n: &[bool], d: usize) -> Vec<bool> {
    let mut p = Vec::from(n);
    p.append(&mut pad(r as isize, n.len() as isize));

    assert!(r < B);

    let mut s = [false; B];
    for chunk in p.chunks(r) {
        for (s_i, c_i) in s.iter_mut().zip(chunk) {
            *s_i ^= c_i;
        }

        s = f(&s);
    }

    let mut z = Vec::<bool>::new();
    while z.len() < d {
        z.extend(&s);

        s = f(&s);
    }

    z.truncate(d);
    z
}

fn keccak(c: usize, n: &[bool], d: usize) -> Vec<bool> {
    sponge(keccak_f, pad101, B - c, n, d)
}

fn h2b(h: &[u8], n: usize) -> Vec<bool> {
    let mut bits = Vec::with_capacity(h.len() * U8BITS);

    for byte in h {
        for i in 0..u8::BITS {
            let mask: u8 = 1 << i;

            bits.push((byte & mask) != 0);
        }
    }

    assert!(bits.len() == h.len() * U8BITS);

    bits.truncate(n);
    bits
}

fn b2h(s: &[bool]) -> Vec<u8> {
    let m = if s.len() % U8BITS != 0 {
        (s.len() / 8) + 1
    } else {
        s.len() / 8
    };
    let mut bytes = vec![0u8; m];

    for (i, bit) in s.iter().enumerate() {
        let byte_index = i / U8BITS;
        let mask = (*bit as u8) << (i % U8BITS);

        bytes[byte_index] |= mask;
    }

    bytes
}
/// Macro to implement all sha3 hash functions as they only differ in digest size
macro_rules! sha3 {
    ($name:ident, $n:literal) => {
        pub fn $name(m: &[u8]) -> [u8; ($n / U8BITS)] {
            let mut temp = h2b(m, m.len() * U8BITS);
            temp.append(&mut vec![false, true]);

            temp = keccak($n * 2, &temp, $n);

            let mut ret = [0u8; ($n / U8BITS)];

            let temp = b2h(&temp);
            assert!(temp.len() == $n / U8BITS);

            for (i, byte) in temp.iter().enumerate() {
                ret[i] = *byte;
            }

            ret
        }
    };
}

sha3!(sha3_224, 224);
sha3!(sha3_256, 256);
sha3!(sha3_384, 384);
sha3!(sha3_512, 512);

#[cfg(test)]
mod tests {
    use super::*;

    macro_rules! digest_test {
        ($fname:ident, $hash:ident, $size:literal, $message:expr, $expected:expr) => {
            #[test]
            fn $fname() {
                let digest = $hash(&$message);

                let expected: [u8; $size / U8BITS] = $expected;

                assert_eq!(digest, expected);
            }
        };
    }

    digest_test!(
        sha3_224_0,
        sha3_224,
        224,
        [0; 0],
        [
            0x6b, 0x4e, 0x03, 0x42, 0x36, 0x67, 0xdb, 0xb7, 0x3b, 0x6e, 0x15, 0x45, 0x4f, 0x0e,
            0xb1, 0xab, 0xd4, 0x59, 0x7f, 0x9a, 0x1b, 0x07, 0x8e, 0x3f, 0x5b, 0x5a, 0x6b, 0xc7,
        ]
    );

    digest_test!(
        sha3_224_8,
        sha3_224,
        224,
        [1u8],
        [
            0x48, 0x82, 0x86, 0xd9, 0xd3, 0x27, 0x16, 0xe5, 0x88, 0x1e, 0xa1, 0xee, 0x51, 0xf3,
            0x6d, 0x36, 0x60, 0xd7, 0x0f, 0x0d, 0xb0, 0x3b, 0x3f, 0x61, 0x2c, 0xe9, 0xed, 0xa4,
        ]
    );

    // Done on large input to verify sponge function is working properly
    digest_test!(
        sha3_224_2312,
        sha3_224,
        224,
        [
            0x31, 0xc8, 0x2d, 0x71, 0x78, 0x5b, 0x7c, 0xa6, 0xb6, 0x51, 0xcb, 0x6c, 0x8c, 0x9a,
            0xd5, 0xe2, 0xac, 0xeb, 0x0b, 0x06, 0x33, 0xc0, 0x88, 0xd3, 0x3a, 0xa2, 0x47, 0xad,
            0xa7, 0xa5, 0x94, 0xff, 0x49, 0x36, 0xc0, 0x23, 0x25, 0x13, 0x19, 0x82, 0x0a, 0x9b,
            0x19, 0xfc, 0x6c, 0x48, 0xde, 0x8a, 0x6f, 0x7a, 0xda, 0x21, 0x41, 0x76, 0xcc, 0xda,
            0xad, 0xae, 0xef, 0x51, 0xed, 0x43, 0x71, 0x4a, 0xc0, 0xc8, 0x26, 0x9b, 0xbd, 0x49,
            0x7e, 0x46, 0xe7, 0x8b, 0xb5, 0xe5, 0x81, 0x96, 0x49, 0x4b, 0x24, 0x71, 0xb1, 0x68,
            0x0e, 0x2d, 0x4c, 0x6d, 0xbd, 0x24, 0x98, 0x31, 0xbd, 0x83, 0xa4, 0xd3, 0xbe, 0x06,
            0xc8, 0xa2, 0xe9, 0x03, 0x93, 0x39, 0x74, 0xaa, 0x05, 0xee, 0x74, 0x8b, 0xfe, 0x6e,
            0xf3, 0x59, 0xf7, 0xa1, 0x43, 0xed, 0xf0, 0xd4, 0x91, 0x8d, 0xa9, 0x16, 0xbd, 0x6f,
            0x15, 0xe2, 0x6a, 0x79, 0x0c, 0xff, 0x51, 0x4b, 0x40, 0xa5, 0xda, 0x7f, 0x72, 0xe1,
            0xed, 0x2f, 0xe6, 0x3a, 0x05, 0xb8, 0x14, 0x95, 0x87, 0xbe, 0xa0, 0x56, 0x53, 0x71,
            0x8c, 0xc8, 0x98, 0x0e, 0xad, 0xbf, 0xec, 0xa8, 0x5b, 0x7c, 0x9c, 0x28, 0x6d, 0xd0,
            0x40, 0x93, 0x65, 0x85, 0x93, 0x8b, 0xe7, 0xf9, 0x82, 0x19, 0x70, 0x0c, 0x83, 0xa9,
            0x44, 0x3c, 0x28, 0x56, 0xa8, 0x0f, 0xf4, 0x68, 0x52, 0xb2, 0x6d, 0x1b, 0x1e, 0xdf,
            0x72, 0xa3, 0x02, 0x03, 0xcf, 0x6c, 0x44, 0xa1, 0x0f, 0xa6, 0xea, 0xf1, 0x92, 0x01,
            0x73, 0xce, 0xdf, 0xb5, 0xc4, 0xcf, 0x3a, 0xc6, 0x65, 0xb3, 0x7a, 0x86, 0xed, 0x02,
            0x15, 0x5b, 0xbb, 0xf1, 0x7d, 0xc2, 0xe7, 0x86, 0xaf, 0x94, 0x78, 0xfe, 0x08, 0x89,
            0xd8, 0x6c, 0x5b, 0xfa, 0x85, 0xa2, 0x42, 0xeb, 0x08, 0x54, 0xb1, 0x48, 0x2b, 0x7b,
            0xd1, 0x6f, 0x67, 0xf8, 0x0b, 0xef, 0x9c, 0x7a, 0x62, 0x8f, 0x05, 0xa1, 0x07, 0x93,
            0x6a, 0x64, 0x27, 0x3a, 0x97, 0xb0, 0x08, 0x8b, 0x0e, 0x51, 0x54, 0x51, 0xf9, 0x16,
            0xb5, 0x65, 0x62, 0x30, 0xa1, 0x2b, 0xa6, 0xdc, 0x78
        ],
        [
            0xaa, 0xb2, 0x3c, 0x9e, 0x7f, 0xb9, 0xd7, 0xda, 0xce, 0xfd, 0xfd, 0x0b, 0x1a, 0xe8,
            0x5a, 0xb1, 0x37, 0x4a, 0xbf, 0xf7, 0xc4, 0xe3, 0xf7, 0x55, 0x6e, 0xca, 0xe4, 0x12
        ]
    );

    digest_test!(
        sha3_256_0,
        sha3_256,
        256,
        [0; 0],
        [
            0xa7, 0xff, 0xc6, 0xf8, 0xbf, 0x1e, 0xd7, 0x66, 0x51, 0xc1, 0x47, 0x56, 0xa0, 0x61,
            0xd6, 0x62, 0xf5, 0x80, 0xff, 0x4d, 0xe4, 0x3b, 0x49, 0xfa, 0x82, 0xd8, 0x0a, 0x4b,
            0x80, 0xf8, 0x43, 0x4a,
        ]
    );

    digest_test!(
        sha3_256_8,
        sha3_256,
        256,
        [0xe9u8],
        [
            0xf0, 0xd0, 0x4d, 0xd1, 0xe6, 0xcf, 0xc2, 0x9a, 0x44, 0x60, 0xd5, 0x21, 0x79, 0x68,
            0x52, 0xf2, 0x5d, 0x9e, 0xf8, 0xd2, 0x8b, 0x44, 0xee, 0x91, 0xff, 0x5b, 0x75, 0x9d,
            0x72, 0xc1, 0xe6, 0xd6,
        ]
    );

    digest_test!(
        sha3_256_2184,
        sha3_256,
        256,
        [
            0xb1, 0xca, 0xa3, 0x96, 0x77, 0x1a, 0x09, 0xa1, 0xdb, 0x9b, 0xc2, 0x05, 0x43, 0xe9,
            0x88, 0xe3, 0x59, 0xd4, 0x7c, 0x2a, 0x61, 0x64, 0x17, 0xbb, 0xca, 0x1b, 0x62, 0xcb,
            0x02, 0x79, 0x6a, 0x88, 0x8f, 0xc6, 0xee, 0xff, 0x5c, 0x0b, 0x5c, 0x3d, 0x50, 0x62,
            0xfc, 0xb4, 0x25, 0x6f, 0x6a, 0xe1, 0x78, 0x2f, 0x49, 0x2c, 0x1c, 0xf0, 0x36, 0x10,
            0xb4, 0xa1, 0xfb, 0x7b, 0x81, 0x4c, 0x05, 0x78, 0x78, 0xe1, 0x19, 0x0b, 0x98, 0x35,
            0x42, 0x5c, 0x7a, 0x4a, 0x0e, 0x18, 0x2a, 0xd1, 0xf9, 0x15, 0x35, 0xed, 0x2a, 0x35,
            0x03, 0x3a, 0x5d, 0x8c, 0x67, 0x0e, 0x21, 0xc5, 0x75, 0xff, 0x43, 0xc1, 0x94, 0xa5,
            0x8a, 0x82, 0xd4, 0xa1, 0xa4, 0x48, 0x81, 0xdd, 0x61, 0xf9, 0xf8, 0x16, 0x1f, 0xc6,
            0xb9, 0x98, 0x86, 0x0c, 0xbe, 0x49, 0x75, 0x78, 0x0b, 0xe9, 0x3b, 0x6f, 0x87, 0x98,
            0x0b, 0xad, 0x0a, 0x99, 0xaa, 0x2c, 0xb7, 0x55, 0x6b, 0x47, 0x8c, 0xa3, 0x5d, 0x1f,
            0x37, 0x46, 0xc3, 0x3e, 0x2b, 0xb7, 0xc4, 0x7a, 0xf4, 0x26, 0x64, 0x1c, 0xc7, 0xbb,
            0xb3, 0x42, 0x5e, 0x21, 0x44, 0x82, 0x03, 0x45, 0xe1, 0xd0, 0xea, 0x5b, 0x7d, 0xa2,
            0xc3, 0x23, 0x6a, 0x52, 0x90, 0x6a, 0xcd, 0xc3, 0xb4, 0xd3, 0x4e, 0x47, 0x4d, 0xd7,
            0x14, 0xc0, 0xc4, 0x0b, 0xf0, 0x06, 0xa3, 0xa1, 0xd8, 0x89, 0xa6, 0x32, 0x98, 0x38,
            0x14, 0xbb, 0xc4, 0xa1, 0x4f, 0xe5, 0xf1, 0x59, 0xaa, 0x89, 0x24, 0x9e, 0x7c, 0x73,
            0x8b, 0x3b, 0x73, 0x66, 0x6b, 0xac, 0x2a, 0x61, 0x5a, 0x83, 0xfd, 0x21, 0xae, 0x0a,
            0x1c, 0xe7, 0x35, 0x2a, 0xde, 0x7b, 0x27, 0x8b, 0x58, 0x71, 0x58, 0xfd, 0x2f, 0xab,
            0xb2, 0x17, 0xaa, 0x1f, 0xe3, 0x1d, 0x0b, 0xda, 0x53, 0x27, 0x20, 0x45, 0x59, 0x80,
            0x15, 0xa8, 0xae, 0x4d, 0x8c, 0xec, 0x22, 0x6f, 0xef, 0xa5, 0x8d, 0xaa, 0x05, 0x50,
            0x09, 0x06, 0xc4, 0xd8, 0x5e, 0x75, 0x67
        ],
        [
            0xcb, 0x56, 0x48, 0xa1, 0xd6, 0x1c, 0x6c, 0x5b, 0xda, 0xcd, 0x96, 0xf8, 0x1c, 0x95,
            0x91, 0xde, 0xbc, 0x39, 0x50, 0xdc, 0xf6, 0x58, 0x14, 0x5b, 0x8d, 0x99, 0x65, 0x70,
            0xba, 0x88, 0x1a, 0x05
        ]
    );

    digest_test!(
        sha3_384_0,
        sha3_384,
        384,
        [0; 0],
        [
            0x0c, 0x63, 0xa7, 0x5b, 0x84, 0x5e, 0x4f, 0x7d, 0x01, 0x10, 0x7d, 0x85, 0x2e, 0x4c,
            0x24, 0x85, 0xc5, 0x1a, 0x50, 0xaa, 0xaa, 0x94, 0xfc, 0x61, 0x99, 0x5e, 0x71, 0xbb,
            0xee, 0x98, 0x3a, 0x2a, 0xc3, 0x71, 0x38, 0x31, 0x26, 0x4a, 0xdb, 0x47, 0xfb, 0x6b,
            0xd1, 0xe0, 0x58, 0xd5, 0xf0, 0x04,
        ]
    );

    digest_test!(
        sha3_384_8,
        sha3_384,
        384,
        [0x80u8],
        [
            0x75, 0x41, 0x38, 0x48, 0x52, 0xe1, 0x0f, 0xf1, 0x0d, 0x5f, 0xb6, 0xa7, 0x21, 0x3a,
            0x4a, 0x6c, 0x15, 0xcc, 0xc8, 0x6d, 0x8b, 0xc1, 0x06, 0x8a, 0xc0, 0x4f, 0x69, 0x27,
            0x71, 0x42, 0x94, 0x4f, 0x4e, 0xe5, 0x0d, 0x91, 0xfd, 0xc5, 0x65, 0x53, 0xdb, 0x06,
            0xb2, 0xf5, 0x03, 0x9c, 0x8a, 0xb7,
        ]
    );

    digest_test!(
        sha3_384_2512,
        sha3_384,
        384,
        [
            0x03, 0x5a, 0xdc, 0xb6, 0x39, 0xe5, 0xf2, 0x8b, 0xb5, 0xc8, 0x86, 0x58, 0xf4, 0x5c,
            0x1c, 0xe0, 0xbe, 0x16, 0xe7, 0xda, 0xfe, 0x08, 0x3b, 0x98, 0xd0, 0xab, 0x45, 0xe8,
            0xdc, 0xdb, 0xfa, 0x38, 0xe3, 0x23, 0x4d, 0xfd, 0x97, 0x3b, 0xa5, 0x55, 0xb0, 0xcf,
            0x8e, 0xea, 0x3c, 0x82, 0xae, 0x1a, 0x36, 0x33, 0xfc, 0x56, 0x5b, 0x7f, 0x2c, 0xc8,
            0x39, 0x87, 0x6d, 0x39, 0x89, 0xf3, 0x57, 0x31, 0xbe, 0x37, 0x1f, 0x60, 0xde, 0x14,
            0x0e, 0x3c, 0x91, 0x62, 0x31, 0xec, 0x78, 0x0e, 0x51, 0x65, 0xbf, 0x5f, 0x25, 0xd3,
            0xf6, 0x7d, 0xc7, 0x3a, 0x1c, 0x33, 0x65, 0x5d, 0xfd, 0xf4, 0x39, 0xdf, 0xbf, 0x1c,
            0xbb, 0xa8, 0xb7, 0x79, 0x15, 0x8a, 0x81, 0x0a, 0xd7, 0x24, 0x4f, 0x06, 0xec, 0x07,
            0x81, 0x20, 0xcd, 0x18, 0x76, 0x0a, 0xf4, 0x36, 0xa2, 0x38, 0x94, 0x1c, 0xe1, 0xe6,
            0x87, 0x88, 0x0b, 0x5c, 0x87, 0x9d, 0xc9, 0x71, 0xa2, 0x85, 0xa7, 0x4e, 0xe8, 0x5c,
            0x6a, 0x74, 0x67, 0x49, 0xa3, 0x01, 0x59, 0xee, 0x84, 0x2e, 0x9b, 0x03, 0xf3, 0x1d,
            0x61, 0x3d, 0xdd, 0xd2, 0x29, 0x75, 0xcd, 0x7f, 0xed, 0x06, 0xbd, 0x04, 0x9d, 0x77,
            0x2c, 0xb6, 0xcc, 0x5a, 0x70, 0x5f, 0xaa, 0x73, 0x4e, 0x87, 0x32, 0x1d, 0xc8, 0xf2,
            0xa4, 0xea, 0x36, 0x6a, 0x36, 0x8a, 0x98, 0xbf, 0x06, 0xee, 0x2b, 0x0b, 0x54, 0xac,
            0x3a, 0x3a, 0xee, 0xa6, 0x37, 0xca, 0xeb, 0xe7, 0x0a, 0xd0, 0x9c, 0xcd, 0xa9, 0x3c,
            0xc0, 0x6d, 0xe9, 0x5d, 0xf7, 0x33, 0x94, 0xa8, 0x7a, 0xc9, 0xbb, 0xb5, 0x08, 0x3a,
            0x4d, 0x8a, 0x24, 0x58, 0xe9, 0x1c, 0x7d, 0x5b, 0xf1, 0x13, 0xae, 0xca, 0xe0, 0xce,
            0x27, 0x9f, 0xdd, 0xa7, 0x6b, 0xa6, 0x90, 0x78, 0x7d, 0x26, 0x34, 0x5e, 0x94, 0xc3,
            0xed, 0xbc, 0x16, 0xa3, 0x5c, 0x83, 0xc4, 0xd0, 0x71, 0xb1, 0x32, 0xdd, 0x81, 0x18,
            0x7b, 0xcd, 0x99, 0x61, 0x32, 0x30, 0x11, 0x50, 0x9c, 0x8f, 0x64, 0x4a, 0x1c, 0x0a,
            0x3f, 0x14, 0xee, 0x40, 0xd7, 0xdd, 0x18, 0x6f, 0x80, 0x7f, 0x9e, 0xdc, 0x7c, 0x02,
            0xf6, 0x76, 0x10, 0x61, 0xbb, 0xb6, 0xdd, 0x91, 0xa6, 0xc9, 0x6e, 0xc0, 0xb9, 0xf1,
            0x0e, 0xdb, 0xbd, 0x29, 0xdc, 0x52
        ],
        [
            0x02, 0x53, 0x5d, 0x86, 0xcc, 0x75, 0x18, 0x48, 0x4a, 0x2a, 0x23, 0x8c, 0x92, 0x1b,
            0x73, 0x9b, 0x17, 0x04, 0xa5, 0x03, 0x70, 0xa2, 0x92, 0x4a, 0xbf, 0x39, 0x95, 0x8c,
            0x59, 0x76, 0xe6, 0x58, 0xdc, 0x5e, 0x87, 0x44, 0x00, 0x63, 0x11, 0x24, 0x59, 0xbd,
            0xdb, 0x40, 0x30, 0x8b, 0x1c, 0x70
        ]
    );

    digest_test!(
        sha3_512_0,
        sha3_512,
        512,
        [0u8; 0],
        [
            0xa6, 0x9f, 0x73, 0xcc, 0xa2, 0x3a, 0x9a, 0xc5, 0xc8, 0xb5, 0x67, 0xdc, 0x18, 0x5a,
            0x75, 0x6e, 0x97, 0xc9, 0x82, 0x16, 0x4f, 0xe2, 0x58, 0x59, 0xe0, 0xd1, 0xdc, 0xc1,
            0x47, 0x5c, 0x80, 0xa6, 0x15, 0xb2, 0x12, 0x3a, 0xf1, 0xf5, 0xf9, 0x4c, 0x11, 0xe3,
            0xe9, 0x40, 0x2c, 0x3a, 0xc5, 0x58, 0xf5, 0x00, 0x19, 0x9d, 0x95, 0xb6, 0xd3, 0xe3,
            0x01, 0x75, 0x85, 0x86, 0x28, 0x1d, 0xcd, 0x26,
        ]
    );

    digest_test!(
        sha3_512_8,
        sha3_512,
        512,
        [0xe5u8],
        [
            0x15, 0x02, 0x40, 0xba, 0xf9, 0x5f, 0xb3, 0x6f, 0x8c, 0xcb, 0x87, 0xa1, 0x9a, 0x41,
            0x76, 0x7e, 0x7a, 0xed, 0x95, 0x12, 0x50, 0x75, 0xa2, 0xb2, 0xdb, 0xba, 0x6e, 0x56,
            0x5e, 0x1c, 0xe8, 0x57, 0x5f, 0x2b, 0x04, 0x2b, 0x62, 0xe2, 0x9a, 0x04, 0xe9, 0x44,
            0x03, 0x14, 0xa8, 0x21, 0xc6, 0x22, 0x41, 0x82, 0x96, 0x4d, 0x8b, 0x55, 0x7b, 0x16,
            0xa4, 0x92, 0xb3, 0x80, 0x6f, 0x4c, 0x39, 0xc1
        ]
    );

    digest_test!(
        sha3_512_4080,
        sha3_512,
        512,
        [
            0x43, 0x02, 0x56, 0x15, 0x52, 0x1d, 0x66, 0xfe, 0x8e, 0xc3, 0xa3, 0xf8, 0xcc, 0xc5,
            0xab, 0xfa, 0xb8, 0x70, 0xa4, 0x62, 0xc6, 0xb3, 0xd1, 0x39, 0x6b, 0x84, 0x62, 0xb9,
            0x8c, 0x7f, 0x91, 0x0c, 0x37, 0xd0, 0xea, 0x57, 0x91, 0x54, 0xea, 0xf7, 0x0f, 0xfb,
            0xcc, 0x0b, 0xe9, 0x71, 0xa0, 0x32, 0xcc, 0xfd, 0x9d, 0x96, 0xd0, 0xa9, 0xb8, 0x29,
            0xa9, 0xa3, 0x76, 0x2e, 0x21, 0xe3, 0xfe, 0xfc, 0xc6, 0x0e, 0x72, 0xfe, 0xdf, 0x9a,
            0x7f, 0xff, 0xa5, 0x34, 0x33, 0xa4, 0xb0, 0x5e, 0x0f, 0x3a, 0xb0, 0x5d, 0x5e, 0xb2,
            0x5d, 0x52, 0xc5, 0xea, 0xb1, 0xa7, 0x1a, 0x2f, 0x54, 0xac, 0x79, 0xff, 0x58, 0x82,
            0x95, 0x13, 0x26, 0x39, 0x4d, 0x9d, 0xb8, 0x35, 0x80, 0xce, 0x09, 0xd6, 0x21, 0x9b,
            0xca, 0x58, 0x8e, 0xc1, 0x57, 0xf7, 0x1d, 0x06, 0xe9, 0x57, 0xf8, 0xc2, 0x0d, 0x24,
            0x2c, 0x9f, 0x55, 0xf5, 0xfc, 0x9d, 0x4d, 0x77, 0x7b, 0x59, 0xb0, 0xc7, 0x5a, 0x8e,
            0xdc, 0x1f, 0xfe, 0xdc, 0x84, 0xb5, 0xd5, 0xc8, 0xa5, 0xe0, 0xeb, 0x05, 0xbb, 0x7d,
            0xb8, 0xf2, 0x34, 0x91, 0x3d, 0x63, 0x25, 0x30, 0x4f, 0xa4, 0x3c, 0x9d, 0x32, 0xbb,
            0xf6, 0xb2, 0x69, 0xee, 0x11, 0x82, 0xcd, 0x85, 0x45, 0x3e, 0xdd, 0xd1, 0x2f, 0x55,
            0x55, 0x6d, 0x8e, 0xdf, 0x02, 0xc4, 0xb1, 0x3c, 0xd4, 0xd3, 0x30, 0xf8, 0x35, 0x31,
            0xdb, 0xf2, 0x99, 0x4c, 0xf0, 0xbe, 0x56, 0xf5, 0x91, 0x47, 0xb7, 0x1f, 0x74, 0xb9,
            0x4b, 0xe3, 0xdd, 0x9e, 0x83, 0xc8, 0xc9, 0x47, 0x7c, 0x42, 0x6c, 0x6d, 0x1a, 0x78,
            0xde, 0x18, 0x56, 0x4a, 0x12, 0xc0, 0xd9, 0x93, 0x07, 0xb2, 0xc9, 0xab, 0x42, 0xb6,
            0xe3, 0x31, 0x7b, 0xef, 0xca, 0x07, 0x97, 0x02, 0x9e, 0x9d, 0xd6, 0x7b, 0xd1, 0x73,
            0x4e, 0x6c, 0x36, 0xd9, 0x98, 0x56, 0x5b, 0xfa, 0xc9, 0x4d, 0x19, 0x18, 0xa3, 0x58,
            0x69, 0x19, 0x0d, 0x17, 0x79, 0x43, 0xc1, 0xa8, 0x00, 0x44, 0x45, 0xca, 0xce, 0x75,
            0x1c, 0x43, 0xa7, 0x5f, 0x3d, 0x80, 0x51, 0x7f, 0xc4, 0x7c, 0xec, 0x46, 0xe8, 0xe3,
            0x82, 0x64, 0x2d, 0x76, 0xdf, 0x46, 0xda, 0xb1, 0xa3, 0xdd, 0xae, 0xab, 0x95, 0xa2,
            0xcf, 0x3f, 0x3a, 0xd7, 0x03, 0x69, 0xa7, 0x0f, 0x22, 0xf2, 0x93, 0xf0, 0xcc, 0x50,
            0xb0, 0x38, 0x57, 0xc8, 0x3c, 0xfe, 0x0b, 0xd5, 0xd2, 0x3b, 0x92, 0xcd, 0x87, 0x88,
            0xaa, 0xc2, 0x32, 0x29, 0x1d, 0xa6, 0x0b, 0x4b, 0xf3, 0xb3, 0x78, 0x8a, 0xe6, 0x0a,
            0x23, 0xb6, 0x16, 0x9b, 0x50, 0xd7, 0xfe, 0x44, 0x6e, 0x6e, 0xa7, 0x3d, 0xeb, 0xfe,
            0x1b, 0xb3, 0x4d, 0xcb, 0x1d, 0xb3, 0x7f, 0xe2, 0x17, 0x4a, 0x68, 0x59, 0x54, 0xeb,
            0xc2, 0xd8, 0x6f, 0x10, 0x2a, 0x59, 0x0c, 0x24, 0x73, 0x2b, 0xc5, 0xa1, 0x40, 0x3d,
            0x68, 0x76, 0xd2, 0x99, 0x5f, 0xab, 0x1e, 0x2f, 0x6f, 0x47, 0x23, 0xd4, 0xa6, 0x72,
            0x7a, 0x8a, 0x8e, 0xd7, 0x2f, 0x02, 0xa7, 0x4c, 0xcf, 0x5f, 0x14, 0xb5, 0xc2, 0x3d,
            0x95, 0x25, 0xdb, 0xf2, 0xb5, 0x47, 0x2e, 0x13, 0x45, 0xfd, 0x22, 0x3b, 0x08, 0x46,
            0xc7, 0x07, 0xb0, 0x65, 0x69, 0x65, 0x09, 0x40, 0x65, 0x0f, 0x75, 0x06, 0x3b, 0x52,
            0x98, 0x14, 0xe5, 0x14, 0x54, 0x1a, 0x67, 0x15, 0xf8, 0x79, 0xa8, 0x75, 0xb4, 0xf0,
            0x80, 0x77, 0x51, 0x78, 0x12, 0x84, 0x1e, 0x6c, 0x5c, 0x73, 0x2e, 0xed, 0x0c, 0x07,
            0xc0, 0x85, 0x95, 0xb9, 0xff, 0x0a, 0x83, 0xb8, 0xec, 0xc6, 0x0b, 0x2f, 0x98, 0xd4,
            0xe7, 0xc6, 0x96, 0xcd, 0x61, 0x6b, 0xb0, 0xa5, 0xad, 0x52, 0xd9, 0xcf, 0x7b, 0x3a,
            0x63, 0xa8, 0xcd, 0xf3, 0x72, 0x12
        ],
        [
            0xe7, 0xba, 0x73, 0x40, 0x7a, 0xa4, 0x56, 0xae, 0xce, 0x21, 0x10, 0x77, 0xd9, 0x20,
            0x87, 0xd5, 0xcd, 0x28, 0x3e, 0x38, 0x68, 0xd2, 0x84, 0xe0, 0x7e, 0xd1, 0x24, 0xb2,
            0x7c, 0xbc, 0x66, 0x4a, 0x6a, 0x47, 0x5a, 0x8d, 0x7b, 0x4c, 0xf6, 0xa8, 0xa4, 0x92,
            0x7e, 0xe0, 0x59, 0xa2, 0x62, 0x6a, 0x4f, 0x98, 0x39, 0x23, 0x36, 0x01, 0x45, 0xb2,
            0x65, 0xeb, 0xfd, 0x4f, 0x5b, 0x3c, 0x44, 0xfd
        ]
    );
}
